package rpc

import (
	"net"
	"net/rpc"
	"sync"
	"util/log"
)

var (
	listen   net.Listener
	services *rpc.Server
)

func RegisterService(serv string, rcvr interface{}) {
	if services == nil {
		services = rpc.NewServer()
	}
	services.RegisterName(serv, rcvr)
}

func CreateService(addr string) error {

	l, err := net.Listen("tcp", addr)
	if err != nil {
		log.LogError("create rpc service failed", err)
		return err
	}
	listen = l

	go func() {

		defer l.Close()

		for {
			conn, err := l.Accept()
			if err != nil {
				break
			}
			//启动服务
			log.LogDebug("new rpc client")
			go services.ServeConn(conn)
		}
	}()

	return nil
}

func StopService() {
	if listen != nil {
		listen.Close()
	}
}

type tcpcall struct {
	conn net.Conn
	cli  *rpc.Client
	addr string
	lock sync.RWMutex
}

func (tc *tcpcall) callFunc(method string, args interface{}, ret interface{}, f func(interface{}, error)) error {
	if tc.conn == nil {
		tc.lock.Lock()
		if tc.conn == nil {
			c, err := net.Dial("tcp", tc.addr)
			if err != nil {
				return err
			}
			tc.conn = c
			tc.cli = rpc.NewClient(tc.conn)
		}
		tc.lock.Unlock()
	}

	tc.lock.RLock()
	go func() {
		tc.lock.RLock()
		err := tc.cli.Call(method, args, ret)
		tc.lock.RUnlock()

		if err != nil {
			tc.lock.Lock()
			tc.conn.Close()
			tc.conn = nil
			tc.cli = nil
			tc.lock.Unlock()
		}

		f(ret, err)
	}()
	tc.lock.RUnlock()

	return nil
}

func (tc *tcpcall) call( method string, args interface{}, ret interface{}) error {
	if tc.conn == nil {
		tc.lock.Lock()
		if tc.conn == nil {
			c, err := net.Dial("tcp", tc.addr)
			if err != nil {
				return err
			}
			tc.conn = c
			tc.cli = rpc.NewClient(tc.conn)
		}
		tc.lock.Unlock()
	}

	tc.lock.RLock()
	err := tc.cli.Call(method, args, ret)
	tc.lock.RUnlock()

	if err != nil {
		tc.lock.Lock()
		tc.conn.Close()
		tc.conn = nil
		tc.cli = nil
		tc.lock.Unlock()
	}

	return err
}

func (tc *tcpcall) notify(method string, args interface{}) error {
	if tc.conn == nil {
		tc.lock.Lock()
		if tc.conn == nil {
			c, err := net.Dial("tcp", tc.addr)
			if err != nil {
				return err
			}
			tc.conn = c
			tc.cli = rpc.NewClient(tc.conn)
		}
		tc.lock.Unlock()
	}

	tc.lock.RLock()
	go func() {
		tc.lock.RLock()
		var ret int8
		err := tc.cli.Call(method, args, &ret)
		tc.lock.RUnlock()

		if err != nil {
			tc.lock.Lock()
			tc.conn.Close()
			tc.conn = nil
			tc.cli = nil
			tc.lock.Unlock()
		}
	}()
	tc.lock.RUnlock()

	return nil
}

func (tc *tcpcall) stop() {
	if tc.conn != nil {
		tc.conn.Close()
		tc.conn = nil
		tc.cli = nil
	}
}

type RpcCall interface {
	callFunc(method string, args interface{}, ret interface{}, f func(interface{}, error)) error
	call( method string, args interface{}, ret interface{}) error
	notify(method string, args interface{}) error
	stop()
}

var (
	rpclist map[string]RpcCall
	lock    sync.Mutex
)

//同步调用
func Call(domain string, method string, args interface{}, ret interface{}) error {
	if _, ok := rpclist[domain]; !ok {
		lock.Lock()
		if _, ok1 := rpclist[domain]; !ok1 {
			rpclist[domain] = &tcpcall{addr: domain}
		}
		lock.Unlock()
	}

	return rpclist[domain].call(method, args, ret)
}

//异步调用,带回调函数
func CallFunc(domain string, method string, args interface{}, ret interface{}, f func(interface{}, error)) error {
	if _, ok := rpclist[domain]; !ok {
		lock.Lock()
		if _, ok1 := rpclist[domain]; !ok1 {
			rpclist[domain] = &tcpcall{addr: domain}
		}
		lock.Unlock()
	}

	return rpclist[domain].callFunc(method, args, ret, f)
}

//异步调用，忽略返回值(被调用函数原型func(interface{}, *int8) error)
func Notify(domain string, method string, args interface{}) error {
	if _, ok := rpclist[domain]; !ok {
		lock.Lock()
		if _, ok1 := rpclist[domain]; !ok1 {
			rpclist[domain] = &tcpcall{addr: domain}
		}
		lock.Unlock()
	}

	return rpclist[domain].notify(method, args)
}

func Stop(domain string) {
	lock.Lock()
	defer lock.Unlock()
	if _, ok := rpclist[domain]; ok {
		rpclist[domain].stop()
	}
}

func init() {
	rpclist = make(map[string]RpcCall)
}
